#ifndef __MY__SERVICE__
#define __MY__SERVICE__
#include <ctime>
#include "data.hpp"
#include "httplib.h"

extern cloud::DataManager *_data;
namespace cloud{
    class Service{
        private:
            int _server_port;
            std::string _server_ip;
            std::string _download_prefix;
            httplib::Server _server;

        private:

            static std::string TimeToStr(const time_t *t)
            {
                std::string time = ctime(t);
                return time;
            }
            //文件上传功能
            static void Upload(const httplib::Request &req, httplib::Response &rsp)
            {
                // post /upload  文件数据在正文中（正文并不全是文件数据)
                auto ret = req.has_file("file");   //判断有没有上传的文件区域
                if(ret == false)
                {
                    rsp.status = 400;
                    return;
                }
                //file.content 文件内容   file.filename 文件名
                const auto& file = req.get_file_value("file");
                std::string back_dir = Config::GetInstance()->GetBackDir();
                std::string realpath = back_dir + FileUtil(file.filename).FileName();

                FileUtil fu(realpath);
                fu.SetContent(&(file.content));    //将数据写入文件中
                
                BackupInfo info;
                info.NewBackupInfo(realpath);      //组织备份的文件信息
                _data->Insert(info);      //向数据管理模块添加备份的文件信息

            }
            // static void ListShow(const httplib::Request &req, httplib::Response &rsp){
            //     //1.获取文件信息
            //     std::vector<BackupInfo> arry;
            //     _data->GetAll(&arry);
            //     //2.设置返回界面格式
            //     std::stringstream ss;
            //     ss<<"<html>";
            //     ss<<"	<head>";
            //     ss<<"		<title>Download</title>";
            //     ss<<"	</head>";
            //     ss<<"	<body>";
            //     ss<<"		<h1>Download</h1>";
            //     ss<<"		<table>";
            //     for(auto &a:arry)
            //     {
            //         FileUtil fu(a.real_path);
            //         ss<<"			<tr>";
            //         ss<<"				<td><a href='"<<a.url<<"'>"<<fu.FileName()<<"</a></td>";
            //         ss<<"				<td align='right'>  "<<ctime(&a.mtime)<<" </td>";
            //         ss<<"				<td align='right'>  "<<a.fsize/1024<<"k </td>";
            //         ss<<"			</tr>";
            //     }
            //     ss<<"		</table>";
            //     ss<<"	</body>";
            //     ss<<"</html>";

            //     rsp.body = ss.str();   //转成string
            //     rsp.set_header("Content-Type", "text/html");
            //     rsp.status = 200;
            //     return;
            // }


            static void ListShow(const httplib::Request &req, httplib::Response &rsp){
                //1.获取文件信息
                std::vector<BackupInfo> arry;
                std::cout<<"1"<<std::endl;
                _data->GetAll(&arry);
                //2.设置返回界面格式
                std::stringstream ss;
                ss<<"<html><head><title>Download</title></head><body><h1>Download</h1><table>";
                for(auto &a:arry)
                {
                    FileUtil fu(a.real_path);
                    ss<<"<tr><td><a href='"<<a.url<<"'>"<<fu.FileName()<<"</a></td>";
                    ss<<"<td align='right'>"<<ctime(&a.mtime)<<" </td>";
                    ss<<"<td align='right'>"<<a.fsize/1024<<"k </td>";
                    ss<<"</tr>";
                }
                ss<<"</table></body></html>";

                rsp.body = ss.str();   //转成string
                rsp.set_header("Content-Type", "text/html");
                rsp.status = 200;
                return;
            }

            static std::string GetEtag(const BackupInfo & info)  //不传文件名是因为，被解压缩的文件是新文件（相当于修改过的）
            {
                //文件名+文件大小+最后修改时间
                FileUtil fu(info.real_path);
                std::string etag = fu.FileName();
                etag+="-";
                etag+=std::to_string(info.fsize);
                etag+="-";
                etag+=std::to_string(info.mtime);

                return etag;
            }


            //断点续传功能
            static void Download(const httplib::Request &req, httplib::Response &rsp){
                //1.获取客户端请求的资源路径path  req.path   这里的资源路径是url
                //2.获取资源路径，获取文件备份信息
                std::cout<<req.path<<std::endl;

                BackupInfo info;
                _data->GetOneByUrl(req.path, &info);
                //3.判断文件是否被压缩，如果被压缩，要先解压缩
                if(info.pack_flag)
                {
                    //4.删除压缩包，修改备份信息(true->false)
                    FileUtil fu(info.pack_path);  //去pack_path里面找
                    fu.UnCompress(info.real_path);  //将文件解压缩要备份目录下,realpath就是文件真实存储的路径
                    fu.Remove();
                    info.pack_flag = false;
                    _data->Update(info);
                }
                //5.读取文件数据，放入rsp.body中
                FileUtil fu(info.real_path);

                //断点续传功能实现
                bool retrans = false;
                std::string old_etag;
                if(req.has_header("If-Range")){
                    old_etag = req.get_header_value("If-Range");
                    //有If-Range字段，并且这个字段的值与请求文件的最新etag一致，则符合断点续传
                    if(old_etag == GetEtag(info))
                        retrans = true;  //文件没被修改过，可以恢复传输
                }
                if(retrans == false){
                    fu.GetContent(&rsp.body);
                    //6.设置响应头部字段，Etag，Accept-Ranges:bytes（用来热点续传的）
                    //content-Length不用设置了，就是body的长度
                    rsp.set_header("Accept-Ranges", "bytes");
                    rsp.set_header("ETag", GetEtag(info));

                    rsp.set_header("Content-Type", "application/octet-stream");   //下载文件的选项
                    rsp.status = 200;  //响应成功
                }
                else   //只应答要求的部分
                {
                    //httplib内部实现了对区间请求也就是断电续传请求的处理
                    //只需要我们用户将文件所有数据读取到rsp.body中，它内部会自动根据请求区间
                    //从body中取出指定区间数据进行响应
                    //std::string range = req.get_header_cal("Range");bytes = start-end
                    fu.GetContent(&rsp.body);
                    //6.设置响应头部字段，Etag，Accept-Ranges:bytes（用来热点续传的）
                    //content-Length不用设置了，就是body的长度
                    rsp.set_header("Accept-Ranges", "bytes");
                    rsp.set_header("ETag", GetEtag(info));
                    //rsp.set_header("Content-Range", "bytes start-end/fsize");
                    rsp.set_header("Content-Type", "application/octet-stream");   //下载文件的选项
                    rsp.status = 206;  //响应成功
                }

                return;
            }

        public:
            Service(){
                Config *config = Config::GetInstance();
                _server_port = config->GetServerPort();
                _server_ip = config->GetServerIP();
                _download_prefix = config->GetDownloadPrefix();
            };
            bool RunModule()
            {
                _server.Post("/upload", Upload);    //post方法 数据放在正文部分
                _server.Get("/listshow", ListShow);  // Get方法 请求参数放在url后面
                _server.Get("/", ListShow);
                std::string download_url = _download_prefix + "(.*)";
                _server.Get(download_url, Download);    //查询正则表达式了解相关规则.*匹配任意字符，任意次

                std::cout<<_server_ip<<std::endl;
                std::cout<<_server_port<<std::endl;
		
                perror("download");
                if(_server.listen(_server_ip.c_str(), _server_port) == false)
                {
                    std::cout<<"listen failed " << std::endl; 
		            perror("listen"); 
                }
                std::cout<<1<<std::endl;
                return true;
            }
    };
}


#endif
